package com.yb.xczx.cloud.auth.oauth2.handler.mybatis;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.ConfigurationSettingNames;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Copyright (C), 2022-2023, 姚兵
 * Author: 32210
 * Date: 2023/5/21 15:58
 * FileName: ClientSettingsTypeHandler
 * Description:
 */
public class TokenSettingsTypeHandler implements TypeHandler<TokenSettings> {
    protected ObjectMapper objectMapper=new ObjectMapper();
    public TokenSettingsTypeHandler(){
        ClassLoader classLoader = JdbcRegisteredClientRepository.class.getClassLoader();
        List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
        this.objectMapper.registerModules(securityModules);
        this.objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
    }
    @Override
    public void setParameter(PreparedStatement ps, int i, TokenSettings parameter, JdbcType jdbcType) throws SQLException {
        if(Objects.isNull(parameter)){
            ps.setString(i,"{}");
            return;
        }

        Map<String, Object> settings = parameter.getSettings();

        if(CollectionUtils.isEmpty(settings)){
            ps.setString(i,"{}");
            return;
        }

            ps.setString(i, writeMap(settings));
    }

    @Override
    public TokenSettings getResult(ResultSet rs, String columnName) throws SQLException {
        String rsString = rs.getString(columnName);
        if(!StringUtils.hasText(rsString)){
            return null;
        }
        Map<String,Object> settings = parseMap(rsString);
        TokenSettings.Builder tokenSettingsBuilder = TokenSettings.withSettings(settings);
        if (!settings.containsKey(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT)) {
            tokenSettingsBuilder.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED);
        }
        return tokenSettingsBuilder.build();
    }

    @Override
    public TokenSettings getResult(ResultSet rs, int columnIndex) throws SQLException {
        String rsString = rs.getString(columnIndex);
        if(!StringUtils.hasText(rsString)){
            return null;
        }

        Map<String,Object> settings = parseMap(rsString);
        TokenSettings.Builder tokenSettingsBuilder = TokenSettings.withSettings(settings);
        if (!settings.containsKey(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT)) {
            tokenSettingsBuilder.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED);
        }
        return tokenSettingsBuilder.build();
    }

    @Override
    public TokenSettings getResult(CallableStatement cs, int columnIndex) throws SQLException {

        String rsString = cs.getString(columnIndex);
        if(!StringUtils.hasText(rsString)){
            return null;
        }

        Map<String,Object> settings = parseMap(rsString);
        TokenSettings.Builder tokenSettingsBuilder = TokenSettings.withSettings(settings);
        if (!settings.containsKey(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT)) {
            tokenSettingsBuilder.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED);
        }
        return tokenSettingsBuilder.build();
    }

    private Map<String, Object> parseMap(String data) {
        try {
            return this.objectMapper.readValue(data, new TypeReference<Map<String, Object>>() {});
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex.getMessage(), ex);
        }
    }


    private String writeMap(Map<String, Object> data) {
        try {
            return this.objectMapper.writeValueAsString(data);
        } catch (Exception ex) {
            throw new IllegalArgumentException(ex.getMessage(), ex);
        }
    }

}
